package com.atguigu.gmall.seckill.task;

import com.atguigu.gmall.model.activity.SeckillGoods;
import com.atguigu.gmall.seckill.mapper.seckillGoodsMapper;
import com.atguigu.gmall.seckill.util.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * 使用定时任务从数据库中把数据存到redis中
 */
@Component
public class AddSeckillGoodsFromDbToRedisTask {
    @Autowired
    RedisTemplate redisTemplate;
    @Resource
    seckillGoodsMapper seckillGoodsMapper;

    /*
    * 定时任务的corn表达式 由左到右为 秒 分 时 天 周 月 年（通常可以省略）
    * * 表示任意时间
    * 具体数字表示指定时间执行  列如 1 3 4 * * * * 表示没一年的每一个月的每一周的 每一天的 每天4点 3分 1秒时执行
    * ？ 表示忽略当前时间 列如 * * 5 ？ * * * 表示每一年的每一月的每一周的不管那一天的5点执行
    * ， 表示多个情况下执行 列如 1,3,5,7 9 * * * * 表示。。。每个小时的第9 分钟的第 1、3、5、7秒 这几个时间点都要执行
    * - 表示这个范围执行  列如 1-9 5 * * * * * 表示。。。每个小时的第5分钟的第1秒 到第9秒 每秒执行一次
    *  / 表示间隔执行 列如 1/30 * * * * * * 表示 。。。。每分钟的第一秒执行后每隔30 秒执行一次
    * fixedDelay 表示每隔多少秒执行一次
    * 列如  fixedDelay = 5000 表示每隔5秒执行一次，
    * 但是他会受方法执行时间的影响，只有等上一个任务执行完成后才执行下一个任务 ，永远保证只有一个任务在执行
    * fixedRate 和上面方法一样 只是他不用等上一个任务执行完成，而是每隔指定时间就会执行，所以他可能会有多个任务同时执行的情况
    * initialDelay  表示指定时间后执行 ，只影响第一次执行，
    *
    **/
    @Scheduled(cron = "1/20 * * * * * ")
    public void addSeckillGoodsFromDbToRedisTask(){
        //计算当前系统时间所在的时间段,以及后面4个时间段
        List<Date> dateMenus = DateUtil.getDateMenus();
        //循环查询每个时间段的商品数据
        dateMenus.stream().forEach(date -> {
            //计算当前这个时间段的开始时间: 2022-11-14 10:00:00
            String startTime = DateUtil.data2str(date, DateUtil.PATTERN_YYYY_MM_DDHHMM);
            //计算当前这个时间段的截止时间: 2022-11-14 12:00:00
            String endTime = DateUtil.data2str(DateUtil.addDateHour(date, 2), DateUtil.PATTERN_YYYY_MM_DDHHMM);
            //计算redis中存储时间段商品的key: 2022111410
            String key = DateUtil.data2str(date, DateUtil.PATTERN_YYYYMMDDHH);
            //拼接查询商品的条件
            LambdaQueryWrapper<SeckillGoods> wrapper = new LambdaQueryWrapper<>();
            //商品必须是审核通过的 "1"
            wrapper.eq(SeckillGoods::getStatus, "1");
            //商品必须在活动时间以内 startTime<=start_time
            wrapper.ge(SeckillGoods::getStartTime, startTime);
            //商品必须在活动时间以内 end_time<=endTime
            wrapper.le(SeckillGoods::getEndTime, endTime);
            //剩余库存必须大于0
            wrapper.gt(SeckillGoods::getStockCount, 0);
            //redis中没有的商品数据
            Set keys = redisTemplate.opsForHash().keys(key);
            if(keys != null && keys.size() > 0){
                wrapper.notIn(SeckillGoods::getId, keys);
            }
            //查询
            List<SeckillGoods> seckillGoodsList = seckillGoodsMapper.selectList(wrapper);
            //遍历将商品数据写入redis中去
            seckillGoodsList.stream().forEach(seckillGoods -> {
                //将商品数据写入redis: TODO----1.商品活动到期需要清理掉 2.商品活动到期需要将剩余库存同步到数据库
                redisTemplate.opsForHash().put(key, seckillGoods.getId() + "", seckillGoods);
            });
        });
    }
}
